import { ArchetypeMaskUtil } from "./Archetype";
import { Component, ComponentPool } from "./Component";
import { Entity, EntityStatus } from "./Entity";
import { EntityGroupManager } from "./EntityGroupManager";
import { EntityManager } from "./EntityManager";
import { Service, ServiceManager } from "./Service";
import { System, SystemManager } from "./System";
import { Trait } from "./Trait";
import { TraitArchetypeCache } from "./TraitCache";
import { EventBus, Event } from "./EventBus";
import { EntityCreatedEvent, EntityRemoveEvent } from "./Events";
import { ComponentViewManager } from "./ViewManager";

export class World {

    // 内部状态
    private _started = false;

    private readonly _entityManager: EntityManager;
    private readonly _componentPool = new ComponentPool();
    private readonly _systemManager: SystemManager;
    private readonly _serviceManager: ServiceManager;
    private readonly _traitCache = new TraitArchetypeCache();
    private readonly _groupManager = new EntityGroupManager();
    private readonly _eventBus = new EventBus();
    private readonly _internalEventBus = new EventBus();
    private readonly _viewManager = new ComponentViewManager();

    // 使用一个临时对象用于遍历
    private _tempEntityArray: Entity[] = [];
    private _tempArchetypeArray: bigint[] = [];

    constructor() {
        this._entityManager = new EntityManager(this);
        this._systemManager = new SystemManager(this);
        this._serviceManager = new ServiceManager(this);
    }

    get entityManager() {
        return this._entityManager;
    }

    get componentPool() {
        return this._componentPool;
    }

    get traitCache() {
        return this._traitCache;
    }

    get groupManager() {
        return this._groupManager;
    }

    get serviceManager() {
        return this._serviceManager;
    }

    get viewManager() {
        return this._viewManager;
    }

    start() {
        if (this._started) {
            throw new Error('can not start twice.');
        }
        this._started = true;
        this._serviceManager.start();
        this._systemManager.start();
    }

    addEntity(initFunc: (entity: Entity) => void) {
        const entity = this._entityManager.createEntity(initFunc);
        this._groupManager.onEntityAdded(entity);

        this.triggerEvent(new EntityCreatedEvent(entity));

        return entity;
    }

    removeEntity(entity: Entity) {
        if (entity.status == EntityStatus.Destroyed) {
            throw new Error('can not remove destroyed entity.');
        }
        this.triggerEvent(new EntityRemoveEvent(entity));
        this._groupManager.onEntityRemove(entity);
        this._entityManager.destroyEntity(entity);
    }

    // 遍历所有entity
    forEachEntity(callback: (entity: Entity) => void) {
        // 收集所有entity
        this._entityManager.getAllEntities(this._tempEntityArray);

        for (let i = 0; i < this._tempEntityArray.length; i++) {
            callback(this._tempEntityArray[i]);
            // 如果entity被销毁，则从tempEntityArray中移除
            if (this._tempEntityArray[i].status == EntityStatus.Destroyed) {
                this._entityManager.destroyEntity(this._tempEntityArray[i]);
            }
        }

        this._tempEntityArray.length = 0;
    }

    // 通过trait遍历
    forEach<T>(traitClass: new () => T, callback: (bundle: T, entity: Entity) => void) {
        // 获取所有类型对应的componentMask
        const trait = Trait.getData(traitClass);

        // 通过mask获取所有的entity (这里可以有个查询缓存，因为archetype本身不变， 而trait本身也不变)
        let archetypeMasks = this._traitCache.get(trait.mask);
        if (!archetypeMasks) {
            this.entityManager.queryArchetypesContains(trait.mask, this._tempArchetypeArray);
            if (this._tempArchetypeArray.length != 0) {
                this._traitCache.add(trait.mask, ...this._tempArchetypeArray);
            } else {
                this._traitCache.addEmpty(trait.mask);
            }

            archetypeMasks = this._tempArchetypeArray;
        }

        if (archetypeMasks.length == 0) {
            return;
        }

        // 临时用的bundle
        const bundle = Trait.getStockBundle(traitClass);

        archetypeMasks.forEach(mask => {
            // 通过mask获取所有的entity
            this._entityManager.getEntitiesByArchetype(mask, this._tempEntityArray);

            // 遍历所有的entity
            if (this._tempEntityArray.length != 0) {
                this._tempEntityArray.forEach(entity => {

                    if (entity.status == EntityStatus.Destroyed) {
                        throw new Error('can not get component from destroyed entity.');
                    }

                    // 从trait里面拿到所有的属性
                    trait.forEachProperty(prop => {
                        const component = entity.getComponentOfMask(prop.typeMask);
                        if (!component) {
                            throw new Error(`component ${prop.propName} not found`);
                        }
                        // 设置bundle
                        bundle[prop.propName] = component;
                    });

                    callback(entity, bundle);
                });
            }
        });

        this._tempArchetypeArray.length = 0;
        this._tempEntityArray.length = 0;
    }

    // 添加system
    addSystem<T extends System>(system: new () => T): T {
        if (this._started) {
            throw new Error('can not add system when started.');
        }
        return this._systemManager.addSystem(system) as T;
    }

    update(deltaTime: number) {
        // 遍历所有的system
        this._systemManager.update(deltaTime);
    }

    // 添加service
    addService<T extends Service>(serviceType: new (world: World, ...args: any[]) => T, ...args: any[]): T {
        if (this._started) {
            throw new Error('can not add service when started.');
        }
        return this._serviceManager.addService(serviceType);
    }

    getService<T extends Service>(serviceType: {prototype:T}): T {
        return this._serviceManager.getService(serviceType);
    }

    // 组管理
    addGroup(id: number, match: (entity: Entity) => boolean) {
        if (this._started) {
            throw new Error('can not add group when started.');
        }
        this._groupManager.newGroup(id, match);
    }

    // 获取组
    getGroup(id: number) {
        return this._groupManager.getGroup(id);
    }

    /**
     * Register an event listener
     * @param eventType The event class to listen for
     * @param handler The callback function to handle the event
     */
    public on<T extends Event>(eventType: new (...args: any[]) => T, handler: (event: T) => void): void {
        this._eventBus.on(eventType, handler);
    }

    /**
     * Remove an event listener
     * @param eventType The event class to remove listener from
     * @param handler The callback function to remove
     */
    public off<T extends Event>(eventType: new (...args: any[]) => T, handler: (event: T) => void): void {
        this._eventBus.off(eventType, handler);
    }

    /**
     * Trigger an event
     * @param event The event to trigger
     */
    public triggerEvent<T extends Event>(event: T): void {
        this._internalEventBus.triggerEvent(event);
        this._eventBus.triggerEvent(event);
    }

    /**
     * Clear all event listeners for a specific type
     * @param eventType The event class to clear
     */
    public clearEvents(eventType?: new (...args: any[]) => Event): void {
        this._eventBus.clear(eventType);
    }

     /**
     * Register an event listener
     * @param eventType The event class to listen for
     * @param handler The callback function to handle the event
     */
     public onInternal<T extends Event>(eventType: new (...args: any[]) => T, handler: (event: T) => void): void {
        this._internalEventBus.on(eventType, handler);
    }

    /**
     * Remove an event listener
     * @param eventType The event class to remove listener from
     * @param handler The callback function to remove
     */
    public offInternal<T extends Event>(eventType: new (...args: any[]) => T, handler: (event: T) => void): void {
        this._internalEventBus.off(eventType, handler);
    }

    /**
     * Trigger an event
     * @param event The event to trigger
     */
    // public triggerInternalEvent<T extends Event>(event: T): void {
    //     this._internalEventBus.triggerEvent(event);
    // }

    /**
     * Clear all event listeners for a specific type
     * @param eventType The event class to clear
     */
    public clearInternalEvents(eventType?: new (...args: any[]) => Event): void {
        this._internalEventBus.clear(eventType);
    }

}